using System.Collections.Generic;
using System.Linq;

using Flood.Log;

namespace Flood
{
	public sealed class Square
	{
		private readonly int hashCode;
		private readonly int maxPuzzleColumns;
		private Square east;
		private Square north;
		private List<Square> physicalAdjacentSquares;
		private Square south;
		private Square west;
		public IEnumerable<Square> AdjacentSquares
		{
			get { return physicalAdjacentSquares; }
		}
		public Square East
		{
			get { return east; }
			set
			{
				east = value;
				UpdatePhysicalAdjacentSquares();
			}
		}

		public int Id { get; private set; }
		public bool IsBorder
		{
			get { return AdjacentSquares.Any(square => square.IsOutOfBounds); }
		}

		public bool IsOutOfBounds
		{
			get { return Shape.Color == GameConstants.OutOfBoundsColor; }
		}

		public Square North
		{
			get { return north; }
			set
			{
				north = value;
				UpdatePhysicalAdjacentSquares();
			}
		}
		public Shape Shape { get; set; }

		public Square South
		{
			get { return south; }
			set
			{
				south = value;
				UpdatePhysicalAdjacentSquares();
			}
		}

		public Square West
		{
			get { return west; }
			set
			{
				west = value;
				UpdatePhysicalAdjacentSquares();
			}
		}

		public Square(int id, char color, int maxPuzzleColumns, IChangeList changeList)
		{
			this.maxPuzzleColumns = maxPuzzleColumns;
			Id = id;
			hashCode = id.GetHashCode();
			Shape = new Shape(id,
			                  color,
			                  this,
			                  changeList);
			UpdatePhysicalAdjacentSquares();
		}

		public override int GetHashCode()
		{
			return hashCode;
		}

		public override string ToString()
		{
			return string.Format("[{0},{1}]",
			                     Id / maxPuzzleColumns,
			                     Id % maxPuzzleColumns);
		}

		private void UpdatePhysicalAdjacentSquares()
		{
			physicalAdjacentSquares =
				new[] { North, South, East, West }
					.Where(square => square != null && !square.IsOutOfBounds)
					.ToList();
		}
	}
}